package com.tobestronger.shouxiespringframework.beans.factory;


import com.tobestronger.shouxiespringframework.beans.BeansException;
import com.tobestronger.shouxiespringframework.beans.PropertyValue;
import com.tobestronger.shouxiespringframework.beans.PropertyValues;
import com.tobestronger.shouxiespringframework.beans.factory.config.BeanDefinition;
import com.tobestronger.shouxiespringframework.beans.factory.config.BeanFactoryPostProcessor;
import com.tobestronger.shouxiespringframework.core.io.DefaultResourceLoader;
import com.tobestronger.shouxiespringframework.core.io.Resource;
import com.tobestronger.shouxiespringframework.util.StringValueResolver;

import java.io.IOException;
import java.util.Properties;

/**
 * 将 bean定义中的占位符 替换为 从location指定的资源中加载的键值对中 对应的值
 */
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {

    /**
     * Default placeholder prefix: {@value}
     */
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

    /**
     * Default placeholder suffix: {@value}
     */
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

    private String location;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        try {
            // 加载属性文件
            DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
            Resource resource = resourceLoader.getResource(location);
            Properties properties = new Properties();
            properties.load(resource.getInputStream());

            //遍历所有beanDefinition
            String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
            for (String beanName : beanDefinitionNames) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

                //查看当前 beanDefinition 的 PropertyValues 中是否存在占位符需要替换
                PropertyValues propertyValues = beanDefinition.getPropertyValues();
                for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                    Object value = propertyValue.getValue();

                    //只处理字符串类型的value
                    if (!(value instanceof String)){
                        continue;
                    }

                    //使用对应属性值替换掉占位符
                    String strVal = (String) value;
                    String resolvedString = resolvePlaceholder(strVal, properties);
                    if (!strVal.equals(resolvedString)){
                        propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), resolvedString));
                    }
                }
            }

            // 向容器中添加字符串解析器，供解析@Value注解使用
            StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties);
            beanFactory.addEmbeddedValueResolver(valueResolver);

        } catch (IOException e) {
            throw new BeansException("Could not load properties", e);
        }
    }

    public void setLocation(String location) {
        this.location = location;
    }

    /**
     * 尝试解析原始字符串中的占位符
     *
     * @param value  原始(可能包含有特殊含义的)字符串
     * @param properties 加载的键值对信息
     * @return 经过处理后的字符串
     */
    private String resolvePlaceholder(String value, Properties properties) {
        String strVal = value;
        StringBuilder buffer = new StringBuilder(strVal);
        int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
        int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
        //判断是否存在 ${} 这样的占位符
        if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {
            //获取key
            String propKey = strVal.substring(startIdx + 2, stopIdx);
            //获取key对应的value
            String propVal = properties.getProperty(propKey);
            //替换原始字符串中的占位符
            buffer.replace(startIdx, stopIdx + 1, propVal);
        }
        return buffer.toString();
    }

    /**
     * 形如${}占位符解析器
     */
    private class PlaceholderResolvingStringValueResolver implements StringValueResolver {

        private final Properties properties;

        public PlaceholderResolvingStringValueResolver(Properties properties) {
            this.properties = properties;
        }

        @Override
        public String resolveStringValue(String strVal) {
            return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);
        }

    }

}
